package com.quan.common.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.codec.binary.Base64;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.CollectionSerializer;
import com.esotericsoftware.kryo.serializers.JavaSerializer;
import com.esotericsoftware.kryo.serializers.MapSerializer;

public class KryoUtil {
	
	public static <T extends Serializable> byte[] serializationObject(T obj) {
		Kryo kryo = new Kryo();
		kryo.setReferences(false);
		kryo.register(obj.getClass(), new JavaSerializer());
		
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		Output output = new Output(baos);
		kryo.writeClassAndObject(output, obj);
		output.flush();
		output.close();
		
		byte[] b = baos.toByteArray();
		try {
			baos.flush();
			baos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return b;
	}
	
	@SuppressWarnings("unchecked")
	public static <T extends Serializable> T deserializationObject(byte[] b, Class<T> clazz) {
		Kryo kryo = new Kryo();
		kryo.setReferences(false);
		kryo.register(clazz, new JavaSerializer());
		
		ByteArrayInputStream bais = new ByteArrayInputStream(b);
		Input input = new Input(bais);
		return (T) kryo.readClassAndObject(input);
	}

	@SuppressWarnings("unchecked")
	public static <T extends Serializable> T deserializationObject(String obj, Class<T> clazz) {
		Kryo kryo = new Kryo();
		kryo.setReferences(false);
		kryo.register(clazz, new JavaSerializer());

		ByteArrayInputStream bais = new ByteArrayInputStream(new Base64().decode(obj));
		Input input = new Input(bais);
		return (T) kryo.readClassAndObject(input);
	}

	public static <T extends Serializable> byte[] serializationList(List<T> obj, Class<T> clazz) {
		Kryo kryo = new Kryo();
		kryo.setReferences(false);
		kryo.setRegistrationRequired(true);

		CollectionSerializer serializer = new CollectionSerializer();
		serializer.setElementClass(clazz, new JavaSerializer());
		serializer.setElementsCanBeNull(false);

		kryo.register(clazz, new JavaSerializer());
		kryo.register(ArrayList.class, serializer);

		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		Output output = new Output(baos);
		kryo.writeObject(output, obj);
		output.flush();
		output.close();

		byte[] b = baos.toByteArray();
		try {
			baos.flush();
			baos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return b;
	}
	
	@SuppressWarnings("unchecked")
	public static <T extends Serializable> List<T> deserializationList(byte[] b, Class<T> clazz) {
		Kryo kryo = new Kryo();
		kryo.setReferences(false);
		kryo.setRegistrationRequired(true);
		
		CollectionSerializer serializer = new CollectionSerializer();
		serializer.setElementClass(clazz, new JavaSerializer());
		serializer.setElementsCanBeNull(false);
		
		kryo.register(clazz, new JavaSerializer());
		kryo.register(ArrayList.class, serializer);
		
		ByteArrayInputStream bais = new ByteArrayInputStream(b);
		Input input = new Input(bais);
		return (List<T>) kryo.readObject(input, ArrayList.class, serializer);
	}

	@SuppressWarnings("unchecked")
	public static <T extends Serializable> List<T> deserializationList(String obj, Class<T> clazz) {
		Kryo kryo = new Kryo();
		kryo.setReferences(false);
		kryo.setRegistrationRequired(true);

		CollectionSerializer serializer = new CollectionSerializer();
		serializer.setElementClass(clazz, new JavaSerializer());
		serializer.setElementsCanBeNull(false);

		kryo.register(clazz, new JavaSerializer());
		kryo.register(ArrayList.class, serializer);

		ByteArrayInputStream bais = new ByteArrayInputStream(new Base64().decode(obj));
		Input input = new Input(bais);
		return (List<T>) kryo.readObject(input, ArrayList.class, serializer);
	}

	public static <T extends Serializable> byte[] serializationMap(Map<String, T> obj, Class<T> clazz) {
		Kryo kryo = new Kryo();
		kryo.setReferences(false);
		kryo.setRegistrationRequired(true);

		MapSerializer serializer = new MapSerializer();
		serializer.setKeyClass(String.class, new JavaSerializer());
		serializer.setKeysCanBeNull(false);
		serializer.setValueClass(clazz, new JavaSerializer());
		serializer.setValuesCanBeNull(true);

		kryo.register(clazz, new JavaSerializer());
		kryo.register(HashMap.class, serializer);

		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		Output output = new Output(baos);
		kryo.writeObject(output, obj);
		output.flush();
		output.close();

		byte[] b = baos.toByteArray();
		try {
			baos.flush();
			baos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return b;
	}
	
	@SuppressWarnings("unchecked")
	public static <T extends Serializable> Map<String, T> deserializationMap(byte[] b, Class<T> clazz) {
		Kryo kryo = new Kryo();
		kryo.setReferences(false);
		kryo.setRegistrationRequired(true);
		
		MapSerializer serializer = new MapSerializer();
		serializer.setKeyClass(String.class, new JavaSerializer());
		serializer.setKeysCanBeNull(false);
		serializer.setValueClass(clazz, new JavaSerializer());
		serializer.setValuesCanBeNull(true);
		
		kryo.register(clazz, new JavaSerializer());
		kryo.register(HashMap.class, serializer);
		
		ByteArrayInputStream bais = new ByteArrayInputStream(b);
		Input input = new Input(bais);
		return (Map<String, T>) kryo.readObject(input, HashMap.class, serializer);
	}

	@SuppressWarnings("unchecked")
	public static <T extends Serializable> Map<String, T> deserializationMap(String obj, Class<T> clazz) {
		Kryo kryo = new Kryo();
		kryo.setReferences(false);
		kryo.setRegistrationRequired(true);

		MapSerializer serializer = new MapSerializer();
		serializer.setKeyClass(String.class, new JavaSerializer());
		serializer.setKeysCanBeNull(false);
		serializer.setValueClass(clazz, new JavaSerializer());
		serializer.setValuesCanBeNull(true);

		kryo.register(clazz, new JavaSerializer());
		kryo.register(HashMap.class, serializer);

		ByteArrayInputStream bais = new ByteArrayInputStream(new Base64().decode(obj));
		Input input = new Input(bais);
		return (Map<String, T>) kryo.readObject(input, HashMap.class, serializer);
	}

	public static <T extends Serializable> byte[] serializationSet(Set<T> obj, Class<T> clazz) {
		Kryo kryo = new Kryo();
		kryo.setReferences(false);
		kryo.setRegistrationRequired(true);

		CollectionSerializer serializer = new CollectionSerializer();
		serializer.setElementClass(clazz, new JavaSerializer());
		serializer.setElementsCanBeNull(false);

		kryo.register(clazz, new JavaSerializer());
		kryo.register(HashSet.class, serializer);

		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		Output output = new Output(baos);
		kryo.writeObject(output, obj);
		output.flush();
		output.close();

		byte[] b = baos.toByteArray();
		try {
			baos.flush();
			baos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return b;
	}
	
	@SuppressWarnings("unchecked")
	public static <T extends Serializable> Set<T> deserializationSet(byte[] b, Class<T> clazz) {
		Kryo kryo = new Kryo();
		kryo.setReferences(false);
		kryo.setRegistrationRequired(true);
		
		CollectionSerializer serializer = new CollectionSerializer();
		serializer.setElementClass(clazz, new JavaSerializer());
		serializer.setElementsCanBeNull(false);
		
		kryo.register(clazz, new JavaSerializer());
		kryo.register(HashSet.class, serializer);
		
		ByteArrayInputStream bais = new ByteArrayInputStream(b);
		Input input = new Input(bais);
		return (Set<T>) kryo.readObject(input, HashSet.class, serializer);
	}

	@SuppressWarnings("unchecked")
	public static <T extends Serializable> Set<T> deserializationSet(String obj, Class<T> clazz) {
		Kryo kryo = new Kryo();
		kryo.setReferences(false);
		kryo.setRegistrationRequired(true);

		CollectionSerializer serializer = new CollectionSerializer();
		serializer.setElementClass(clazz, new JavaSerializer());
		serializer.setElementsCanBeNull(false);

		kryo.register(clazz, new JavaSerializer());
		kryo.register(HashSet.class, serializer);

		ByteArrayInputStream bais = new ByteArrayInputStream(new Base64().decode(obj));
		Input input = new Input(bais);
		return (Set<T>) kryo.readObject(input, HashSet.class, serializer);
	}
	
	/**
	 * 将字节数组转换成字符串
	 * @param b
	 * @return
	 */
	public static String converterString(byte[] b) {
		return new String(new Base64().encode(b));
	}
	
}
